package com.rongji.dfish.ui.widget;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import com.rongji.dfish.base.Utils;
import com.rongji.dfish.base.util.BeanUtil;
import com.rongji.dfish.ui.MultiContainer;
import com.rongji.dfish.ui.Widget;
import com.rongji.dfish.ui.form.AbstractBox;
import com.rongji.dfish.ui.form.Triplebox;
import com.rongji.dfish.ui.json.J;
import com.rongji.dfish.ui.layout.AbstractLayout;



/**
 * Leaf 树节点 在3.0以前的版本叫TreeItem
 * <p>
 * 它允许有0个到多个子节点<br/>
 * 默认的属性有
 * <ul>
 * <ol><b>pk</b> 逻辑编号，字符串</ol>
 * <ol><b>t</b> 显示文本</ol>
 * <ol><b>src</b> 展开时向服务端获取XML的URL</ol>
 * <ol><b>ic</b> 图标</ol>
 * <ol><b>oic</b> 展开时的图标</ol>
 * <ol><b>act</b> 点击动作</ol>
 * <ol><b>menu</b> 右键时调用的命令</ol>
 * </ul>
 *</p>
 * @author DFish Team
 * @version 2.0
 * @since XMLTMPL 1.0
 */
public class Leaf extends AbstractLayout<Leaf,Leaf>implements MultiContainer<Leaf,Leaf> {
	private Boolean focus;
	private String icon;
	private String openicon;
	private Boolean open;
    private Object text;
    private Object tip;
    private String src;
//    private Leaf pub;
    private AbstractBox<?> box;
 

    /**
     * 默认构造函数,因为树经常没有下级,但一般有属性,所默认创建属性的list
     */
    public Leaf() {
    	super(null);
    }
    /**
     * @param id 
     * @param text
     */
    public Leaf(String id,String text) {
    	super(id);
    	this.setText(text);
    }

    /**
     * @param id 
     * @param text
     */
    public Leaf(String id, Widget<?> text) {
    	super(id);
    	this.setText(text);
    }
 
    /**
     * 在这棵树下找到第一个id为指定id的节点
     * (注意:这里的id就是属性里面的id)
     * @param id String
     * @return Tree
     */
	public List<Leaf> findPathById(String id) {
    	LinkedList<Leaf> result=new LinkedList<Leaf>();
    	findPathById(result,this,id);
    	return result;
    }
    private static boolean findPathById(LinkedList<Leaf> path,Leaf tree,String pkid){
    	path.add(tree);
    	if (pkid == null && tree.getId() == null) {
            return true;
        }
        if (pkid != null && pkid.equals(tree.getId())) {
            return true;
        }
        if (tree.nodes != null) {
            for (Leaf elem : tree.nodes) {
                if (findPathById(path,elem,pkid)) {
                    return true;
                }
            }
        }
    	path.removeLast();
    	return false;
    }
    
    /**
     * 输出本节点的路径，主要用于调试
     * @param path 路径
     * @return String
     */
    public static String toString(List<Leaf> path){
    	StringBuilder sb=new StringBuilder();
    	sb.append("path:[");
    	for (Iterator<Leaf> iter = path.iterator(); iter.hasNext();) {
			Leaf element =  iter.next();
			sb.append(element.getText())
				.append('(')
				.append(element.getId())
				.append(')');
			if(iter.hasNext()){
				sb.append(" - ");
			}
		}
    	sb.append(']');
    	return sb.toString();
    }

    /**
     * 添加另一颗树的子树
     * @param otherTree Tree
     * @return Tree
     */
    public Leaf addAllSubLeaf(Leaf otherTree) {
        if (nodes == null) {
            nodes = new ArrayList<Leaf>();
        }
        if (otherTree.nodes != null) {
            nodes.addAll(otherTree.nodes);
        }
        return this;
    }

    /**
     * 删除一颗树的子树
     * @return Tree
     */
    public Leaf clearAllSubLeaf() {
    	nodes.clear();//help gc
        nodes = null;
        return this;
    }

    /**
     * 复制另一颗树的内容,包括属性和子树.
     * 注意是浅拷贝,改动这个树的内容将会影响原先的树.
     * @param otherLeaf Tree
     */
    public void copyFromAnotherLeaf(Leaf otherLeaf) {
//        this.dataPart = otherLeaf.dataPart;
    	this.data=otherLeaf.data;
        this.nodes = otherLeaf.nodes;
    }

    /**
     * 复制另一颗树的内容,包括属性和子树.
     * 注意是深拷贝,速度比较慢.
     * @param otherLeaf Tree
     */
    public void copyFromAnotherLeafSafe(Leaf otherLeaf) {
    	this.data=new LinkedHashMap<String,Object>(otherLeaf.data);
        if (otherLeaf.nodes != null) {
            for (Leaf elem : otherLeaf.nodes) {
                Leaf t = new Leaf();
                t.copyFromAnotherLeafSafe(elem);
                add(t);
            }
        }
    }


    /**
     * 添加一个或多个节点
     * @param subTree 子节点
     * @return 自身
     * @deprecated 2.x的写法已经不再支持
     */
    @Deprecated
    public Leaf addTreeItem(Leaf ... subTree) {
        if (subTree != null && subTree.length > 0) {
            for (int i = 0; i < subTree.length; i++) {
               add(subTree[i]);
            }
        }
        return this;
    }

	
	public String getType() {
		return null;//"widget/tree";
	}




	/**
	 * 选项表单，类型是 checkbox 或 radio。取消或勾选这个box，将同步fieldset内部所有表单的状态。
	 * @return AbstractBox
	 */
	public AbstractBox<?> getBox() {
	return box;
}


	/**
	 * 选项表单，类型是 checkbox 或 radio。取消或勾选这个box，将同步fieldset内部所有表单的状态。
	 * @param box
	 * @return 本身，这样可以继续设置其他属性
	 */
	public Leaf setBox(AbstractBox<?> box) {
		this.box = box;
		return this;
	}

    /**
     * 是否焦点状态。
     * @return focus
     */
    public Boolean getFocus() {
		return focus;
	}
	/**
	 * 是否焦点状态。
	 * @param focus
	 * @return 本身，这样可以继续设置其他属性
	 */
	public Leaf setFocus(Boolean focus) {
		this.focus = focus;
		return this;
	}
	/**
	 * 图标。可使用图片url地址，或以 "." 开头的样式名。
	 * @return icon
	 */
	public String getIcon() {
		return icon;
	}
	/**
	 * 图标。可使用图片url地址，或以 "." 开头的样式名。
	 * @param icon
	 * @return 本身，这样可以继续设置其他属性
	 */
	public Leaf setIcon(String icon) {
		this.icon = icon;
		return this;
	}
	/**
	 * 展开图标
	 * @return openicon
	 */
	public String getOpenicon() {
		return openicon;
	}
	/**
	 * 展开图标
	 * @param openicon
	 * @return 本身，这样可以继续设置其他属性
	 */
	public Leaf setOpenicon(String openicon) {
		this.openicon = openicon;
		return this;
	}



	/**
	 * 是否展开状态。
	 * @return open
	 */
	public Boolean getOpen() {
		return open;
	}
	/**
	 * 是否展开状态。
	 * @param open
	 * @return 本身，这样可以继续设置其他属性
	 */
	public Leaf setOpen(Boolean open) {
		this.open = open;
		return this;
	}
	/**
	 * 显示文本。
	 * @return text
	 */
	public Object getText() {
		return text;
	}
	/**
	 * 显示文本。
	 * @param text
	 * @return 本身，这样可以继续设置其他属性
	 */ 
	public Leaf setText(String text) {
		this.text = text;
		return this;
	}
	/**
	 * 显示文本。
	 * @param text
	 * @return 本身，这样可以继续设置其他属性
	 */ 
	public Leaf setText(Widget<?> text) {
		this.text = text;
		return this;
	}
	/**
	 * 提示信息。设为true，提示信息将使用text参数的值。
	 * @return tip
	 */
	public Object getTip() {
		return tip;
	}
	/**
	 * 提示信息。设为true，提示信息将使用text参数的值。
	 * @param tip
	 * @return 本身，这样可以继续设置其他属性
	 */
	public Leaf setTip(Boolean tip) {
		this.tip = tip;
		return this;
	}
	/**
	 * 提示信息。设为true，提示信息将使用text参数的值。
	 * @param tip
	 * @return 本身，这样可以继续设置其他属性
	 */
	public Leaf setTip(String tip) {
		this.tip = tip;
		return this;
	}
	/**
	 * 获取子节点的URL地址。
	 * @return src
	 */
	public String getSrc() {
		return src;
	}
	/**
	 * 获取子节点的URL地址。
	 * @param src
	 * @return 本身，这样可以继续设置其他属性
	 */
	public Leaf setSrc(String src) {
		this.src = src;
		return this;
	}
	@Override
	public List<Leaf> getNodes() {
		return nodes;
	}
	private static String[] TEXT_PROP_NAMES={"text","value"};
	/**
	 * 取得标签的文本
	 * @return String
	 */
	public String getTextAsString(){
		if(text==null){
			return null;
		}
		if(text instanceof String){
			return (String) text;
		}else if(text instanceof Widget){
			Widget <?> w=(Widget <?>)text;
			try {
				for(String propName:TEXT_PROP_NAMES){
					Object o=BeanUtil.getProperty(w, propName);
					if(o!=null){
						return o.toString();
					}
				}
			} catch (Exception e) {}
		}
		return null;
	}
	static{
		J.get(Leaf.class).removeProperty("textAsString");
	}
	
	/**
	 * 级联修复选择框的状态，仅支持Triplebox的状态修复，且根据{@link Triplebox#getChecked()} 来判断，true代表该节点被选中
	 * @return 本身，这样可以继续设置其他属性
	 */
	public Leaf fixBoxCheckStatus() {
		if (this.getBox() == null || !(this.getBox() instanceof Triplebox)) {
			// 无选择框或者选择框不是Triplebox 直接返回无需修复
			return this;
		}
		
		fixBoxCheckStatusCascade();
		
	    return this;
    }
	
	/**
	 * 级联修复下级选择框的选中状态
	 * @return this
	 */
	private int fixBoxCheckStatusCascade() {
		
		Triplebox triplebox = (Triplebox) this.getBox();
		
		List<Leaf> sugList = getNodes();
		// 默认未选中
		int status = Triplebox.STATUS_UNCHECKED;
		if (Utils.notEmpty(sugList)) { // 下级节点不为空的情况
			// 子节点的选中状态
			Set<Integer> subStatusSet = new HashSet<Integer>(3);
			for (Leaf sub : sugList) {
				int subStatus = sub.fixBoxCheckStatusCascade();
				subStatusSet.add(subStatus);
			}
			if (subStatusSet.size() > 1) {
				// 至少2种状态说明肯定是,当前级肯定是半选状态
				status = Triplebox.STATUS_PARTIALCHECKED;
			} else {
				// 理论上不可能出现为空的情况,故不进行判断
				for (Integer subStatus : subStatusSet) {
					// 其他情况下级存什么状态,他的上级也是什么状态
					status = subStatus;
				}
			}
		} else { // 没有下级节点
			// 使用这个方法必须设置checked
			Integer boxStatus = triplebox.getStatus();
			if (boxStatus != null && boxStatus == Triplebox.STATUS_CHECKED) {
				// 选中
				status = Triplebox.STATUS_CHECKED;
			}
		}
		// 设置选中状态
		triplebox.setStatus(status);
		
		return status;
	}

}
